--[[
	ReaLua examples to get you started scripting
	REAPER with Lua!

	They are loaded by the default init.lua, or
	you can load 'em up with

	dofile(reaper.realua.luadir .. "\\examples.lua")

	These examples rely on the functions defined in
	init.lua.

	The example scripts are loaded into the global space,
	so they can usuallualy be executed like this,

	tt = TracksTable()

	The long comments that begin with the "dofile()" are meant
	to be copied and pasted into the ReaLua Window Input Area,
	and show how to run more examples - Do Mo' Stuff!
]]


--[[	Quick record arm/clear demostrate power of trax():

	rac({3},true)	arms track 3
	ra {2,3,4}		arms tracks 2,3,4
	rc "piano"		clears rec arm on all tracks with "piano" in them
]]

function rac(lit, bac)
	for k,v in next, trax(lit) do reaper.CSurf_OnRecArmChange(v, bac) end
end
function ra(lit) rac(lit, true) end
function rc(lit) rac(lit, false) end

function oneliner()
	loadstring(input("By Your Command"))()
end

reaper.addMenuCommand("oneliner()", "&Command", 1)

--[[
		Collect data into Lua
		tables for display, file
		write, whatever::
]]

-- all the params for the fx in one table
-- each element is { name, curval }

function FXParamsTable(mt, fxn)
	local pt = {}
	for j = 0, reaper.TrackFX_GetNumParams(mt, fxn) do
		local ns = reaper.TrackFX_GetParamName(mt, fxn, j)
		if ns then
			local curv = reaper.TrackFX_GetParam(mt, fxn, j)
			pt[j] = { name = ns, curval = curv }
		end
	end

	return pt
end

-- all the FX for the track in one table
-- each element is { name, id, {params} }

function TrackFXTable(mt)
	local fxt = {}
	for j=0, reaper.TrackFX_GetCount(mt) do
		local ns = reaper.TrackFX_GetFXName(mt, j)
		if ns then
			fxt[j] = {
				name = ns, id = j,
				params = FXParamsTable(mt, j)
			}
--			ptable(fxt[j])
		end
	end
	return fxt
end

-- the vital specifics for each track
-- { name, id, volume, pan, {fx} }

function TrackVitals(mt)
	local ns = reaper.MediaTrackInfo.GetName(mt) or "MASTER"
	local vol = reaper.MediaTrackInfo.GetVol(mt)
	local panv = reaper.MediaTrackInfo.GetPan(mt)
	local tn = reaper.CSurf_TrackToID(mt, true)
	return
		{	name = ns, id = tn, volume = vol, pan = panv,
			fx = TrackFXTable(mt)
		}
end

-- all the tracks vitals in one table

function TracksTable()
	local tracks = {}
	for mt in ialltrax(true) do
		table.insert(tracks, TrackVitals(mt))
	end
	return tracks
end

--[[
		A single print() happens in computer time
		Repeated print()s happen in human time
		Therefore if the slowness of print()s is
		a problem, just gang up the strings
		and print() it all at once at the end
]]

function PrintTracksTable(tt)
	local s = ""
	for k,tr in opairs(tt) do
		s = s .. sprint( "Track: ", tr.id, tr.name,
						 "vol: " .. tr.volume, "pan: " .. tr.pan )

			for kf, fx in opairs(tr.fx) do
					s = s .. sprint( "\tFX: ", fx.id, fx.name )

					for kp, param in opairs(fx.params) do
							s = s .. sprint( "\t\t Param " .. kp, param.name, param.curval )
					end
			end
	end
	print(s)
end

-- May be useful to make it into skinny html!

function TracksTable2Html(tt)
	-- row template stamper for Track row
	local function ths(tr)	-- track html string
		local th = "\t<tr>\r\n" ..
		"\t\t<td>Track:</td><td>trid</td><td><b>trname</b></td>\r\n" ..
		"\t\t<td>vol: trvolume</td>\r\n" ..
		"\t\t<td>pan: trpan</td>\r\n" ..
		"\t</tr>\r\n"

		th = string.gsub(th, "trid", tr.id)
		th = string.gsub(th, "trname", tr.name)
		th = string.gsub(th, "trvolume", tr.volume)
		th = string.gsub(th, "trpan", tr.pan)

		return th
	end

	-- row template stamper for Effect row
	local function fhs(fx)	-- fx html string
		local fh = "\t<tr>\r\n" ..
		"\t\t<td></td><td>Effect:</td><td>fxid</td>\r\n" ..
		"\t\t<td><b>fxname</b></td>\r\n" ..
		"\t</tr>\r\n"

		fh = string.gsub(fh, "fxid", fx.id)
		fh = string.gsub(fh, "fxname", fx.name)

		return fh
	end

	-- row template stamper for Param row
	local function phs(kp, param)	-- param html string
		ph = "\t<tr>\r\n" ..
		"\t\t<td></td><td></td>\r\n" ..
		"\t\t<td>Param:</td><td>kp</td>\r\n" ..
		"\t\t<td>paramname</td>\r\n" ..
		"\t\t<td>paramcurval</td>\r\n" ..
		"\t</tr>\r\n"

		ph = string.gsub(ph, "kp", kp)
		ph = string.gsub(ph, "paramname", param.name)
		ph = string.gsub(ph, "paramcurval", param.curval)

		return ph
	end

	-- begin accumulating our html
	local ah = '<table border="1">\r\n'

	for k,tr in opairs(tt) do
		local th = ths(tr)
		for kf, fx in opairs(tr.fx) do
			local fh = fhs(fx)
			for kp, param in opairs(fx.params) do
				local ph = phs(kp, param)
				fh = fh .. ph
			end
			th = th .. fh
		end
		ah = ah .. th
	end
	ah = ah .. "</table>"
	return ah
end

-- put it in a file

function TracksTable2HtmlFile(tt)
	local h = TracksTable2Html(tt)
	local ns = input("Save to:", "N:\\tracks.html")
	local f = io.open(ns, "wb")	-- 'b' so we control eols
	f:write(h)
	f:close()
end

function ShowTracksTable()
	require("wx")
	if wx == nil then return end
	local ht = TracksTable2Html(TracksTable())

	local frame = wx.wxFrame(	wx.NULL, wx.wxID_ANY, "Tracks",
								wx.wxDefaultPosition, wx.wxSize(800, 600),
								wx.wxDEFAULT_FRAME_STYLE )

	frame:Connect(wx.wxID_EXIT, wx.wxEVT_COMMAND_MENU_SELECTED,
		function (event)
			frame:Close(true)
		end )


	local hp = wx.wxLuaHtmlWindow(frame)
	hp:SetPage(ht)
	frame:Show(true)
	wx.wxGetApp():MainLoop()
end

--[[
tt = TracksTable()
PrintTracksTable(tt)
print(TracksTable2Html(tt))
TracksTable2HtmlFile(tt)
]]

function wxd(f)
	require("wx")
	local wxPath = "lib\\wx\\samples"
	local wxExt = ".wx.lua"
	dofile(wxPath .. "\\" .. f .. wxExt)
end


--[[
	reaper.addMenuCommand("ShowTracksTable()","Track Details")
	reaper.addMenuCommand("wxd 'calculator'","Calculator")
	reaper.addMenuCommand("wxd 'dialog'", "Temperature")
-- Because you need to know the absolute temperature
-- of your studio!!
]]

require("pan")

for k in pairs(pan) do
	if k:sub(1,1) ~= "_" then
		reaper.addMenuCommand(	"pan." .. k .. "()",
								"Pan " .. k )
	end
end

-- select only one track (this is not trax-aware)

function SelectOnly(tn)
	for i = 0, reaper.GetNumTracks() do
		local desel = reaper.CSurf_TrackFromID( i, false )
		reaper.SetTrackSelected( desel, false )
	end
	local sel = reaper.CSurf_TrackFromID( tn, false )
	reaper.SetTrackSelected( sel, true )
end

-- _Enums map nicely to Lua iterators:

-- iterate over the given reaper enum function

function ireanum(f)
	local function iter(f, i)
		i = i + 1
		local nxt, desc = f(i)
		if nxt ~= 0 then
			return i, desc
		end
	end

	return iter, f, -1
end

-- so we can use it in fors

function PrintSinks()
	for nxt, desc in ireanum(reaper.PCM_Sink_Enum) do
		print( "PCM_Sink " .. nxt, desc )
	end
end

function PrintPitchModes()
	-- iterate over Pitch Shift Modes
	local function imodes()
		local mdx = -1
		return function()
			mdx = mdx + 1
			local mode = reaper.EnumPitchShiftModes(mdx)
			if mode then
				return mdx, mode
			end
		end
	end
	-- iterate over Pitch Shift SubModes
	local function isubmodes(mdx)
		local sdx = -1
		return function()
			sdx = sdx + 1
			local submode = reaper.EnumPitchShiftSubModes(mdx, sdx)
			if submode then
				return sdx, submode
			end
		end
	end

	-- use iterators nestedly
	local s = ""
	for mdx, mode in imodes(reaper.EnumPitchShiftModes) do
		s = s .. sprint("PitShfMode " .. mdx, mode)
		for sdx, submode in isubmodes(mdx) do
			s = s .. sprint("\tPitSubMode " .. sdx, submode)
		end
	end
	print(s)
end

function PrintProjectMarkers()
	local function imarks()
		local mdx = -1
		return function()
			mdx = mdx + 1
			return reaper.EnumProjectMarkers(mdx)
		end
	end

	print(string.format("%8s %8s %8s %s", "Index", "Marker", "Position", "Name"))
	for tm in imarks() do
		print(string.format("%8d %8d %8.3f %s",
				tm.index, tm.markrgnindexnumber,
				tm.pos, tm.name))
	end
end

function ShowProjectMarkers()
	window()
	PrintProjectMarkers()
end

reaper.addMenuCommand(	"ShowProjectMarkers()",
						"List Project Markers")

--[[
PrintProjectMarkers()
PrintSinks()
PrintPitchModes()
]]

function PrintFileInfo( filename )
	ps = reaper.PCM_Source_CreateFromFile( filename )

	print("File Name:\t" .. ps:GetFileName() )
	print("Length (s):\t" .. ps:GetLength() )
	print("Channels:\t" .. ps:GetNumChannels() )
	print("Rate:\t\t" .. ps:GetSampleRate() )

	ps:Delete()
end

--[[ use the reaper.keystack(...) function to
 send keystrokes to the Reaper window.  You might want to
 use this instead of reaper.Main_OnCommand() as the keystrokes
 do not change as much as the command numbers do.  They are
 also easier to remember!
]]

function TogClock()
	reaper.keystack( reaper.VK_MENU, string.byte("C") ) -- ALT-C
end

function SaveAs()
	reaper.keystack( reaper.VK_CONTROL,
					 reaper.VK_MENU,
					 string.byte("S") ) -- CTRL-ALT-S
end

function quit()
	reaper.keystack( reaper.VK_CONTROL, string.byte("Q") )	-- CTRL-Q
end

-- http://shup.com/Shup/34485/reaper_command-identifiers.txt

-- use the reaper.Main_OnCommand(number, arg) function to
-- send commands to Reaper

function WrapCommand(number, arg)
	return function()
		reaper.Main_OnCommand(number, arg)
	end
end

-- they can be in the global space

SeeFX = WrapCommand(40291, 0)	-- Track: View FX chain for current track

-- wrapped commands in locals make things legibler!

function DriveNuts()
	local rewind = WrapCommand(40042, 0)	-- Transport: Go to start of project
	local play = WrapCommand(1007, 0)		-- Transport: Play
	local pause = WrapCommand(1008, 0)		-- Transport: Pause
	local stop = WrapCommand(1016, 0)		-- Transport: Stop

	rewind()
	for i = 1,10 do
		play()
		reaper.sleep(math.random() * 1000)
		pause()
		reaper.sleep(math.random() * 1000)
	end
	stop()
end

-- let's even call xome of Xenakios'ses commands!

xebase = 53003	-- this number changes depending on currently installed plugins

function xenafil()
	local function xecmd(cid)
		return WrapCommand( cid + xebase )
	end

	local choose = xecmd(17, 0)	-- Xenakios Extensions : Choose Files For Random Insert...
	local insert = xecmd(18, 0)	-- Xenakios Extensions : Insert Random File

	choose()
	insert()
end

-- play "scripts" of commands

function scripto(t)
	for k,v in opairs(t) do
		reaper.Main_OnCommand(v, 0)
	end
end

-- pick a random file to add in a new track
-- randomize all track colors, rewind and play

function PickNPlay()
	scripto{
		40297,  -- Track: Unselect all tracks
		17 + xebase,	-- Xenakios Extensions : Choose Files For Random Insert...
		18 + xebase,	-- Xenakios Extensions : Insert Random File
		40108,  -- Item Properties: Normalize items
		40296,  -- Track: Select all tracks
		40358,  -- Track: Set selected track colors to random colors
		40042,	-- Transport: Go to start of project
		1007	-- Transport: Play
	}
end

--[[ or the illegible, un-maintainable!:
scripto{40297, 53020, 53021, 40296, 40358, 40108, 40042, 1007}
]]

--[[
TogClock()
SaveAs()
]]

function InsertMediaHardWay( filename )
  if reaper.GetNumTracks() < 1 then
	reaper.InsertTrackAtIndex( 1 , true )
  end

-- Okay we get a MediaTrack
	mt = reaper.CSurf_TrackFromID( 1 , false )
-- then we get a new MediaItem
	mi = reaper.AddMediaItemToTrack(mt)

-- this one has no MediaItem_Takeses so
	mit = reaper.AddTakeToMediaItem(mi)

	ps = reaper.PCM_Source_CreateFromFile( filename )
	reaper.MediaItemTakeInfo.SetSource(mit,ps)

	reaper.MediaItemInfo.SetLength(mi, ps:GetLength() )
end

-- sink = reaper.PCM_Sink_CreateMIDIFile("wah.mid", "", 0, 66, 5)

function PrintMidiInputs()
	for idx = 0, reaper.GetNumMIDIInputs() do
		local ns = reaper.GetMIDIInputName(idx)
		if ns then
			print("Midi In " .. idx, ns)
		end
	end
end

function PrintMidiOutputs()
	for idx = 0, reaper.GetNumMIDIOutputs() do
		local ns = reaper.GetMIDIOutputName(idx)
		if ns then
			print("Midi Out " .. idx, ns)
		end
	end
end

function PrintResampleModes()
	local function iresamplemodes()
		local mdx = -1
		return function()
			mdx = mdx + 1
			local ns = reaper.Resample_EnumModes(mdx)
			if ns then return mdx, ns end
		end
	end

	for idx, ns in iresamplemodes() do
		print("Resample Mode " .. idx, ns)
	end
end

function PrintInputChannelNames()
	local idx = -1
	repeat
		idx = idx + 1
		local ns = reaper.GetInputChannelName(idx)
		if ns then
			print("Input Channel " .. idx, ns)
		end
	until ns == nil
end

function PrintOutputChannelNames()
	local idx = -1
	repeat
		idx = idx + 1
		local ns = reaper.GetOutputChannelName(idx)
		if ns then
			print("Output Channel " .. idx, ns)
		end
	until ns == nil
end

--[[
PrintMidiInputs()
PrintMidiOutputs()
PrintResampleModes()
PrintInputChannelNames()
PrintOutputChannelNames()
]]

function StartMidiRec( inid )
	mip = reaper.CreateMIDIInput(inid); -- print(mip)

	mip:start()
	mip:RunPreNoteTracking(1)

	return mip
end

function StopMidiRec( mip, ts )
	mip:stop()
	mip:SwapBufs(ts)
	mel = mip:GetReadBuf(); -- print(mel:GetSize())
	return mel
end

function MidiEvents(mel)
	local id = 0
	local ml = mel
	return function()
		local nxt, met = ml:EnumItems(id)
		id = id + 1
		if nxt ~= 0 then
			return id-1, met
		end
	end
end

function PrintMidiEvents(mel)
	for id, met in MidiEvents(mel) do
		print( "Midi Event " .. id, met )
	end
end

--[[
PrintMidiInputs()
mn = tonumber( input("Enter a Midi Input number", "1") )
mi = StartMidiRec(mn)
sn = tonumber( input("Play something\r\nEnter a timestamp") )
ml = StopMidiRec( mi, sn )
PrintMidiEvents(ml)
]]

function propen()
  require('lfs')
  if lfs then
    p = "Open Project In\r\n" .. lfs.currentdir()
    reaper.Main_openProject( input( p, "rowboat.rpp" ) )
    reaper.CSurf_OnPlay()
    input( "Hit Return to Stop It!" )
    reaper.CSurf_OnStop()
  end
end

--[[
tt = TracksTable()
PrintTracksTable(tt)
PrintSinks()
PrintPitchModes()
PrintMidiInputs()
PrintMidiOutputs()
PrintResampleModes()
PrintInputChannelNames()
PrintOutputChannelNames()
]]


-- lua for midi heppers

-- takes a Lua number and returns
-- a table of its 7-bit wyrds

function turn7(num)
	local t = {}

	while num >= 0x80 do		-- does not fit in seven bits
		local low7 = num % 0x80		-- isolate the low 7 bits
--		print("\n" , low7)
		table.insert(t, 1, low7)

		local rest = num - low7		-- "mask" out low 7 bits
		num = rest / 0x80 		-- shift right 7 bits
	end
	table.insert(t, 1, num)			-- the last low seven bits

	return t
end

function check7(num)
	local t = turn7(num)
	local sum = 0
	for k,v in opairs(t) do
		sum = sum + v * 128 ^ (#t - k)
	end
	print("num: " .. num .. " sum: " .. sum .. " equal " .. tostring(num == sum):upper())
end

-- returns a Lua string containing
-- a MIDI "variable-length" value

function evl(num)
	local tbl = turn7(num)

	for i=1, # tbl - 1 do
		tbl[i] = tbl[i] + 0x80	-- set high bit means "byte(s) to follow"
	end

	return string.char( unpack(tbl) )
end

function playNote(nn)
	local nnn = nn or 0x7F
	local mo = reaper.CreateMIDIOutput(3,false)


	local non = reaper.makeMidiEvent(0, 0x90, nnn, 0x7F)
	local nof = reaper.makeMidiEvent(0, 0x80, nnn, 0x7F)
	local mel = reaper.makeMidiEventList()
	mel:AddItem(non)
	mel:AddItem(nof)
end


-- ntlua.setenv("LUA_PATH", [[!\Plugins\lua\?.lua;!\Plugins\lua\?\?.lua]])
-- ntlua.setenv("LUA_CPATH", [[!\Plugins\lua\?.dll;!\Plugins\lua\?\?.dll]])

